﻿using System.Windows.Forms;
using HDM.NexusEngine.Core;

namespace HDM.NexusEngine.Input
{
    public class Mouse
    {
        private readonly Form _parentForm;
        private readonly Control _openGLControl;
        private bool _leftClickDetect;
        private bool _rightClickDetect;
        private bool _middleClickDetect;
        private Point _previousPosition;
        private bool _wheelDeltaChanged;
        /// <summary>
        /// The direction and amount that the mouse point has been moved in one frame.
        /// </summary>
        private Vector _moveDelta;

        public Point Position { get; set; }
        public bool LeftPressed { get; private set; }
        public bool RightPressed { get; private set; }
        public bool MiddlePressed { get; private set; }
        public bool LeftHeld { get; private set; }
        public bool RightHeld { get; private set; }
        public bool MiddleHeld { get; private set; }
        public int Wheel { get; set; }
        public Vector MoveDelta { get { return _moveDelta; } }

        public Mouse(Form form, Control openGLControl)
        {
            _parentForm = form;
            _openGLControl = openGLControl;
            Position = new Point();

            _openGLControl.MouseClick += delegate(object obj, MouseEventArgs e)
            {
                switch (e.Button)
                {
                    case MouseButtons.Left:
                        _leftClickDetect = true;
                        break;
                    case MouseButtons.Right:
                        _rightClickDetect = true;
                        break;
                    case MouseButtons.Middle:
                        _middleClickDetect = true;
                        break;
                }
            };

            _openGLControl.MouseDown += delegate(object obj, MouseEventArgs e)
            {
                switch (e.Button)
                {
                    case MouseButtons.Left:
                        LeftHeld = true;
                        break;
                    case MouseButtons.Right:
                        RightHeld = true;
                        break;
                    case MouseButtons.Middle:
                        MiddleHeld = true;
                        break;
                }
            };

            _openGLControl.MouseUp += delegate(object obj, MouseEventArgs e)
            {
                switch (e.Button)
                {
                    case MouseButtons.Left:
                        LeftHeld = false;
                        break;
                    case MouseButtons.Right:
                        RightHeld = false;
                        break;
                    case MouseButtons.Middle:
                        MiddleHeld = false;
                        break;
                }
            };

            _openGLControl.MouseLeave += delegate
            {
                // If you move the mouse out the window then release all held buttons.
                LeftHeld = false;
                RightHeld = false;
                MiddleHeld = false;
            };

            _openGLControl.MouseWheel += delegate(object obj, MouseEventArgs e)
            {
                _wheelDeltaChanged = true;
                Wheel = e.Delta;
            };
        }

        public void Update(double elapsedTime)
        {
            Update(elapsedTime, Vector.Zero, _parentForm.ClientSize.Width, _parentForm.ClientSize.Height);
        }

        /// <summary>
        /// Update with information about the OpenGL view port size.
        /// </summary>
        public void Update(double elapsedTime, Vector origin, double glWidth, double glHeight)
        {
            _previousPosition = Position;
            Position = GetMousePosition(origin, glWidth, glHeight);
            _moveDelta.X = Position.X - _previousPosition.X;
            _moveDelta.Y = Position.Y - _previousPosition.Y;
            UpdateMouseButtons();
            UpdateMouseWheel();
        }

        private void UpdateMouseWheel()
        {
            if (_wheelDeltaChanged)
            {
                _wheelDeltaChanged = false;
            }
            else
            {
                Wheel = 0;
            }
        }

        private Point GetMousePosition(Vector origin, double width, double height)
        {
            System.Drawing.Point mousePos = Cursor.Position;
            mousePos = _openGLControl.PointToClient(mousePos);

            var x = mousePos.X / ((double)_openGLControl.ClientSize.Width);
            var y = mousePos.Y / ((double)_openGLControl.ClientSize.Height);

            var adjustedMousePoint = new Point
            {
                X = (float) Interpolation.Lerp(x, 0, 1, origin.X - (width/2), origin.X + (width/2)),
                Y = (float) Interpolation.Lerp(y, 0, 1, origin.Y + (height/2), origin.Y - (height/2))
            };
            return adjustedMousePoint;
        }

        private void UpdateMouseButtons()
        {
            // reset buttons
            MiddlePressed = false;
            LeftPressed = false;
            RightPressed = false;

            if (_leftClickDetect)
            {
                LeftPressed = true;
                _leftClickDetect = false;
            }

            if (_rightClickDetect)
            {
                RightPressed = true;
                _rightClickDetect = false;
            }

            if (_middleClickDetect)
            {
                MiddlePressed = true;
                _middleClickDetect = false;
            }
        }
    }
}